package perl.compiler;

import java.rmi.server.Operation;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import perl.lexical.classes.Delimeter;
import perl.lexical.classes.Identifier;
import perl.lexical.classes.Keyword;
import perl.lexical.classes.Operator;
import perl.lexical.classes.PNumber;
import perl.lexical.classes.PString;
import perl.lexical.classes.Token;
import perl.lexical.classes.TokenClass;
import perl.lexical.classes.TokenClassEnum;

public class LexicalAnalyzer {

	private final static Pattern numberPattern = Pattern.compile("^\\s*[0-9]$");
	private final static Pattern stringPattern = Pattern.compile("^[\"\\']$");
	private final static Pattern delimeterPattern = Pattern
			.compile("^[\\(\\)\\{\\}\\;\\,\\.\\#]$");
	private final static Pattern symbolPattern = Pattern.compile("^\\s*[\\$\\@\\%]$");
	private final static Pattern charPattern = Pattern.compile("^\\s*[a-zA-Z]$");
	private final static Pattern blankPattern = Pattern.compile("^\\s++$");
	private final static Pattern operatorPattern = Pattern
			.compile("^\\s*[\\+\\-\\*\\/\\==\\!=\\=\\>\\<\\<=\\>=]$");
	
	private int row;
	private int col;
	private int i = 0;
	private static Map<TokenClassEnum, TokenClass> analiziers = new HashMap<TokenClassEnum, TokenClass>();

	static {
		// Add analisiers
		analiziers.put(TokenClassEnum.KEYWORD, new Keyword());
		analiziers.put(TokenClassEnum.OPERATOR, new Operator());
		analiziers.put(TokenClassEnum.IDENTIFIER, new Identifier());
		analiziers.put(TokenClassEnum.DELIMETER, new Delimeter());
		analiziers.put(TokenClassEnum.PSTRING, new PString());
		analiziers.put(TokenClassEnum.PNUMBER, new PNumber());
	}

	public Map<Integer, Token> analize(List<StringBuilder> codeLines) throws Exception {
		row = 1;
		col = 1;
		i = 0;
		Map<Integer, Token> tokens = new TreeMap<Integer, Token>();
		
		//check path to interpreter
		if(checkPathToInterpreter(codeLines.get(0))){
			codeLines.remove(0);
		}else{
			throw new Exception("ERORR: wrong path to interpreter");
		}
		
		for (StringBuilder code : codeLines) {
			col = 1;
			row++;

			while (code.length() > 0) {

				String ch = code.substring(0, 1);

				// if BLANK  - skip
				if (blankPattern.matcher(ch).matches()) {

					col++;
					code.delete(0, 1);

					continue;
				}
				
				// if COMMENT - skip
				if(ch.equals(Delimeter.SHARP)){
					break;
				}
					
				// KEYWORD
				if (charPattern.matcher(ch).matches()) {

					Token token = analiziers.get(TokenClassEnum.KEYWORD).generateToken(
							code.toString());

					if (token != null) {
						createToken(token, tokens, code);
						continue;
					}
				}

				// IDENTIFIER
				if (symbolPattern.matcher(ch).matches()
						|| charPattern.matcher(ch).matches()) {

					Token token = analiziers.get(TokenClassEnum.IDENTIFIER).generateToken(
							code.toString());

					if (token != null) {
						createToken(token, tokens, code);
						continue;
					}
				}

				// OPERATOR
				if (operatorPattern.matcher(ch).matches()) {

					Token token = analiziers.get(TokenClassEnum.OPERATOR).generateToken(
							code.toString());

					if (token != null) {
						createToken(token, tokens, code);
						continue;
					}

				}

				// STRING CONSTANTS
				if (stringPattern.matcher(ch).matches()) {

					Token token = analiziers.get(TokenClassEnum.PSTRING).generateToken(
							code.toString());

					if (token != null) {
						createToken(token, tokens, code);
						continue;
					}

				}

				// NUMBER CONSTANTS
				if (numberPattern.matcher(ch).matches()) {

					Token token = analiziers.get(TokenClassEnum.PNUMBER).generateToken(
							code.toString());

					if (token != null) {
						createToken(token, tokens, code);
						continue;
					}

				}

				// DELIMETERS
				if (delimeterPattern.matcher(ch).matches()) {

					Token token = analiziers.get(TokenClassEnum.DELIMETER).generateToken(
							code.toString());

					if (token != null) {
						createToken(token, tokens, code);	
						continue;
					}
					
				}
				
				throw new Exception("ERORR: lexical error at row: " + row + "; col: " + col);
			}
		}
		return tokens;
	}

	private boolean checkPathToInterpreter(StringBuilder code) {
		
		Pattern p = Pattern.compile("\\s*\\#\\!.*");
		Matcher m = p.matcher(code.toString());
		
		return m.matches();
		
	}

	private void createToken(Token token, Map<Integer, Token> tokens,
			StringBuilder code) {
		i++;
		token.setCol(col);
		col += token.getLexem().length();
		token.setRow(row);
		tokens.put(i, token);

		code.delete(0, token.getLexem().length());
	}
}
